// -------------------------------------------------------------------------
//    @FileName         :    NFCLoginNet_ServerModule.cpp
//    @Author           :    LvSheng.Huang
//    @Date             :    2013-01-02
//    @Module           :    NFCLoginNet_ServerModule
//    @Desc             :
// -------------------------------------------------------------------------

#include "NFCLoginNet_ServerModule.h"
#include "NFLoginNet_ServerPlugin.h"
#include "NFComm/NFMessageDefine/NFProtocolDefine.hpp"

bool NFCLoginNet_ServerModule::Init()
{
    m_pNetServerModule = pPluginManager->FindModule<NFINetServerModule>();

    m_pNetModule = m_pNetServerModule->GetServerNetInfo(NF_ST_LOGIN);

    m_pKernelModule = pPluginManager->FindModule<NFIKernelModule>();
    m_pLogModule = pPluginManager->FindModule<NFILogModule>();
    m_pClassModule = pPluginManager->FindModule<NFIClassModule>();
    m_pElementModule = pPluginManager->FindModule<NFIElementModule>();
    m_pLoginToMasterModule = pPluginManager->FindModule<NFILoginToMasterModule>();
    m_pAccountRedisModule = pPluginManager->FindModule<NFIAccountDBModule>();
    return true;
}

bool NFCLoginNet_ServerModule::Finalize()
{
    return true;
}

bool NFCLoginNet_ServerModule::Shut()
{
    return true;
}

bool NFCLoginNet_ServerModule::BeforeShut()
{
    return true;
}

bool NFCLoginNet_ServerModule::AfterInit()
{
    m_pNetModule->AddReceiveCallBack(NFMsg::EGMI_STS_HEART_BEAT, this, &NFCLoginNet_ServerModule::OnHeartBeat);
    m_pNetModule->AddReceiveCallBack(NFMsg::EGMI_REQ_LOGIN, this, &NFCLoginNet_ServerModule::OnLoginProcess);
    m_pNetModule->AddReceiveCallBack(NFMsg::EGMI_REQ_LOGOUT, this, &NFCLoginNet_ServerModule::OnLogOut);
    m_pNetModule->AddReceiveCallBack(NFMsg::EGMI_REQ_CONNECT_WORLD, this, &NFCLoginNet_ServerModule::OnSelectWorldProcess);
    m_pNetModule->AddReceiveCallBack(NFMsg::EGMI_REQ_WORLD_LIST, this, &NFCLoginNet_ServerModule::OnViewWorldProcess);
    m_pNetModule->AddReceiveCallBack(this, &NFCLoginNet_ServerModule::InvalidMessage);

    m_pNetModule->AddEventCallBack(this, &NFCLoginNet_ServerModule::OnSocketClientEvent);
    m_pNetModule->ExpandBufferSize();

    NF_SHARE_PTR<NFIClass> xLogicClass = m_pClassModule->GetElement(NFrame::Server::ThisName());
    if (xLogicClass)
    {
        const std::vector<std::string>& strIdList = xLogicClass->GetIDList();
        for (int i = 0; i < (int)strIdList.size(); ++i)
        {
            const std::string& strId = strIdList[i];

            const int nServerType = m_pElementModule->GetPropertyInt(strId, NFrame::Server::Type());
            const int nServerID = m_pElementModule->GetPropertyInt(strId, NFrame::Server::ServerID());

            if (nServerType == NF_SERVER_TYPES::NF_ST_LOGIN && pPluginManager->GetAppID(NF_SERVER_TYPES::NF_ST_LOGIN) == nServerID)
            {
                const int nPort = m_pElementModule->GetPropertyInt(strId, NFrame::Server::Port());
                const int nMaxConnect = m_pElementModule->GetPropertyInt(strId, NFrame::Server::MaxOnline());
                const int nCpus = m_pElementModule->GetPropertyInt(strId, NFrame::Server::CpuCount());

                int nRet = m_pNetModule->Initialization(nMaxConnect, nPort, nCpus);
                if (nRet < 0)
                {
                    std::ostringstream strLog;
                    strLog << "Cannot init server net, Port = " << nPort;
                    m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, NULL_OBJECT, strLog, __FUNCTION__, __LINE__);
                    NFASSERT(nRet, "Cannot init server net", __FILE__, __FUNCTION__);
                    exit(0);
                }
            }
        }
    }

    return true;
}

int NFCLoginNet_ServerModule::OnSelectWorldResultsProcess(const int nWorldID, const NFGUID xSenderID, const int nLoginID, const std::string& strAccount, const std::string& strWorldIP, const int nWorldPort, const std::string& strWorldKey)
{
    NF_SHARE_PTR<LoginRoleInfo> pLoginRoleInfo = mxClientIdent.GetElement(xSenderID);
    if (pLoginRoleInfo == nullptr)
    {
        return -1;
    }

    pLoginRoleInfo->state = LOGIN_STATE_FINISH;

    NFMapEx<int, NFMsg::ServerInfoReport>& xWorldMap = m_pLoginToMasterModule->GetWorldMap();
    NFMsg::ServerInfoReport* pWorldData = xWorldMap.FirstNude();
    if (xWorldMap.Count() == 1 && pWorldData)
    {
        NFMsg::AckAccountLogin xData;
        xData.set_event_code(NFMsg::EGEC_SUCCESS);
        *xData.mutable_role_id() = NFINetModule::NFToPB(pLoginRoleInfo->roleId);

        ::NFMsg::WorldServerInfo* pWorldServerInfo = xData.mutable_world_server_info();
        if (pWorldServerInfo)
        {
            pWorldServerInfo->mutable_world_info()->set_name(pWorldData->server_name());
            pWorldServerInfo->mutable_world_info()->set_status(pWorldData->server_state());
            pWorldServerInfo->mutable_world_info()->set_server_id(pWorldData->server_id());
            pWorldServerInfo->mutable_world_info()->set_wait_count(0);

            pWorldServerInfo->set_world_ip(strWorldIP);
            pWorldServerInfo->set_world_port(nWorldPort);
            pWorldServerInfo->set_world_key(strWorldKey);
        }

        m_pNetModule->ProxyServerSendMsgPBToClient(NFMsg::EGameMsgID::EGMI_ACK_LOGIN, xData, pLoginRoleInfo->sockIndex);
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, xSenderID, "Login successed :", strAccount, __FUNCTION__, __LINE__);
    }
    else
    {
        if (pLoginRoleInfo)
        {
            NFMsg::AckConnectWorldResult xMsg;
            xMsg.set_world_id(nWorldID);
            xMsg.mutable_sender()->CopyFrom(NFINetModule::NFToPB(xSenderID));
            xMsg.set_login_id(nLoginID);
            xMsg.set_account(strAccount);
            xMsg.set_world_ip(strWorldIP);
            xMsg.set_world_port(nWorldPort);
            xMsg.set_world_key(strWorldKey);

            m_pNetModule->ProxyServerSendMsgPBToClient(NFMsg::EGameMsgID::EGMI_ACK_CONNECT_WORLD, xMsg, pLoginRoleInfo->sockIndex);
        }
    }

    return 0;
}

bool NFCLoginNet_ServerModule::Execute()
{
    LoginStateHandle();
    return true;
}

void NFCLoginNet_ServerModule::OnClientConnected(const int nAddress)
{
    NetObject* pObject = m_pNetModule->GetNet()->GetNetObject(nAddress);
    if (pObject)
    {
        NFGUID xIdent = m_pKernelModule->CreateGUID();
        pObject->SetClientID(xIdent);
        NF_SHARE_PTR<LoginRoleInfo> pLoginRoleInfo = NF_SHARE_PTR<LoginRoleInfo>(NF_NEW LoginRoleInfo());
        if (pLoginRoleInfo)
        {
            pLoginRoleInfo->callTampTime = NFGetTime();
            pLoginRoleInfo->clientId = xIdent;
            pLoginRoleInfo->sockIndex = nAddress;
            pLoginRoleInfo->state = LOGIN_STATE_CONNECT;
        }
        mxClientIdent.AddElement(xIdent, pLoginRoleInfo);
    }
}

void NFCLoginNet_ServerModule::OnClientDisconnect(const int nAddress)
{
    NetObject* pObject = m_pNetModule->GetNet()->GetNetObject(nAddress);
    if (pObject)
    {
        NFGUID xIdent = pObject->GetClientID();
        mxClientIdent.RemoveElement(xIdent);
    }
}

NF_SHARE_PTR<LoginRoleInfo> NFCLoginNet_ServerModule::GetLoginRoleInfo(const NFGUID& clientId)
{
    return mxClientIdent.GetElement(clientId);
}

void NFCLoginNet_ServerModule::LoginStateHandle()
{
    std::vector<NFGUID> delvec;
    LoginRoleInfo* pInfo = mxClientIdent.FirstNude();
    while (pInfo)
    {
        int32_t micSec = (int32_t)(NFGetTime() - pInfo->callTampTime);
        int32_t maxTime = 0;

        if (pInfo->state == LOGIN_STATE_CONNECT)
        {
            maxTime = MAX_CONNECT_TIME;
        }
        else if (pInfo->state > LOGIN_STATE_CONNECT && pInfo->state < LOGIN_STATE_FINISH)
        {
            maxTime = MAX_CB_TIME;
        }

        if (micSec >= maxTime)
        {
            delvec.push_back(pInfo->clientId);
            m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, pInfo->clientId, "Login call time out,"
                                    , "state:" + lexical_cast<std::string>(pInfo->state) + " useTime:" + lexical_cast<std::string>(micSec), __FUNCTION__, __LINE__);
        }

        if (pInfo->state == LOGIN_STATE_FINISH)
        {
            delvec.push_back(pInfo->clientId);
        }

        pInfo = mxClientIdent.NextNude();
    }

    for (auto it = delvec.begin(); it != delvec.end(); it++)
    {
        LoginRoleInfo* pInfo = mxClientIdent.GetElementNude(*it);
        if (pInfo)
        {
            NetObject* pNetObject = m_pNetModule->GetNet()->GetNetObject(pInfo->sockIndex);
            if (pNetObject)
            {
                pNetObject->SetNeedRemove(true);
            }
        }
        mxClientIdent.RemoveElement(*it);
    }

    return;
}

void NFCLoginNet_ServerModule::LoginProcessGetAccountInfoFromMysql(const NFGUID& self, const int nRet, const std::vector<std::string>& fieldVec, const std::vector<std::string>& valueVec, const std::string& strUseData)
{
    NF_SHARE_PTR<LoginRoleInfo> pLoginRoleInfo = GetLoginRoleInfo(self);
    if (pLoginRoleInfo == nullptr)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, self, "GetLoginRoleInfo(self)", "", __FUNCTION__, __LINE__);
        return;
    }

    NFGUID nRoleID;

    //mysql no data
    if (nRet != 0)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, self, "mysql can't find the account:" + pLoginRoleInfo->account, "", __FUNCTION__, __LINE__);
        nRoleID = m_pKernelModule->CreateGUID();
        m_pAccountRedisModule->CreateAccountInfoToRedis(pLoginRoleInfo->account, nRoleID);
        m_pAccountRedisModule->CreateAccountInfoToMysql(pLoginRoleInfo->account, nRoleID);

        pLoginRoleInfo->state = LOGIN_STATE_SELECT_WORLD;
        pLoginRoleInfo->roleId = nRoleID;
    }
    else
    {
        std::string account;
        m_pAccountRedisModule->RecvAsynGetAccountInfoFromMysql(fieldVec, valueVec, account, nRoleID);

        if (account != pLoginRoleInfo->account)
        {
            m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, self, "Mysql Query Error:", "account is not right!", __FUNCTION__, __LINE__);
            return;
        }

        if (nRoleID.IsNull())
        {
            m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, self, "Mysql Query Error:", "nRoleID is not right!", __FUNCTION__, __LINE__);
            nRoleID = m_pKernelModule->CreateGUID();
            m_pAccountRedisModule->CreateAccountInfoToRedis(pLoginRoleInfo->account, nRoleID);
            m_pAccountRedisModule->CreateAccountInfoToMysql(pLoginRoleInfo->account, nRoleID);
        }

        pLoginRoleInfo->state = LOGIN_STATE_SELECT_WORLD;
        pLoginRoleInfo->roleId = nRoleID;
        m_pAccountRedisModule->CreateAccountInfoToRedis(pLoginRoleInfo->account, nRoleID);
    }

    NFMapEx<int, NFMsg::ServerInfoReport>& xWorldMap = m_pLoginToMasterModule->GetWorldMap();
    if (xWorldMap.Count() == 1)
    {
        NFMsg::ServerInfoReport* pWorldData = xWorldMap.FirstNude();
        if (pWorldData)
        {
            NFMsg::ReqConnectWorld xData;
            xData.set_world_id(pWorldData->server_id());
            xData.set_login_id(pPluginManager->GetAppID(NF_ST_LOGIN));
            xData.mutable_sender()->CopyFrom(NFINetModule::NFToPB(pLoginRoleInfo->clientId));
            xData.set_account(pLoginRoleInfo->account);

            m_pLoginToMasterModule->SendMsgPBToMaster(pLoginRoleInfo->account, NFMsg::EGameMsgID::EGMI_REQ_CONNECT_WORLD, xData);
        }
    }
    else
    {
        NFMsg::AckAccountLogin xData;
        xData.set_event_code(NFMsg::EGEC_SUCCESS);
        *xData.mutable_role_id() = (NFINetModule::NFToPB(nRoleID));

        m_pNetModule->ProxyServerSendMsgPBToClient(NFMsg::EGameMsgID::EGMI_ACK_LOGIN, xData, pLoginRoleInfo->sockIndex);
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, self, "Login successed :", pLoginRoleInfo->account, __FUNCTION__, __LINE__);
    }
    return;
}

void NFCLoginNet_ServerModule::OnLoginProcess(const int nSockIndex, const int nMsgID, const char* msg, const uint32_t nLen)
{
    NFMsg::ReqAccountLogin xMsg;
    if (!m_pNetModule->ProxyServerReceivePBFromClient(nSockIndex, nMsgID, msg, nLen, xMsg))
    {
        return;
    }

    NetObject* pNetObject = m_pNetModule->GetNet()->GetNetObject(nSockIndex);
    if (pNetObject)
    {
        pNetObject->SetConnectKeyState(1);

        NF_SHARE_PTR<LoginRoleInfo> pLoginRoleInfo = GetLoginRoleInfo(pNetObject->GetClientID());
        if (pLoginRoleInfo == nullptr)
        {
            m_pLogModule->LogNormal(NFILogModule::NLL_ERROR_NORMAL, NFGUID(0, nSockIndex), "Can't find the LoginRoleInfo from pNetObject->GetClientID()", "",  __FUNCTION__, __LINE__);
            return;
        }

        pLoginRoleInfo->state = LOGIN_STATE_REQ_GUID;
        pLoginRoleInfo->account = xMsg.account();

        NFGUID nRoleID;
        m_pAccountRedisModule->LoadAccountInfoFromRedis(xMsg.account(), nRoleID);
        if (nRoleID.IsNull())
        {
            //get account info from mysql
            m_pAccountRedisModule->SendAsynGetAccountInfoFromMysql(pLoginRoleInfo->clientId, xMsg.account(), 0, this, &NFCLoginNet_ServerModule::LoginProcessGetAccountInfoFromMysql);
            return;
        }

        pLoginRoleInfo->state = LOGIN_STATE_SELECT_WORLD;
        pLoginRoleInfo->roleId = nRoleID;

        NFMapEx<int, NFMsg::ServerInfoReport>& xWorldMap = m_pLoginToMasterModule->GetWorldMap();
        if (xWorldMap.Count() == 1)
        {
            NFMsg::ServerInfoReport* pWorldData = xWorldMap.FirstNude();
            if (pWorldData)
            {
                NFMsg::ReqConnectWorld xData;
                xData.set_world_id(pWorldData->server_id());
                xData.set_login_id(pPluginManager->GetAppID(NF_ST_LOGIN));
                xData.mutable_sender()->CopyFrom(NFINetModule::NFToPB(pLoginRoleInfo->clientId));
                xData.set_account(pLoginRoleInfo->account);

                m_pLoginToMasterModule->SendMsgPBToMaster(pLoginRoleInfo->account, NFMsg::EGameMsgID::EGMI_REQ_CONNECT_WORLD, xData);//here has a problem to be solve
            }
        }
        else
        {
            NFMsg::AckAccountLogin xData;
            xData.set_event_code(NFMsg::EGEC_SUCCESS);
            *xData.mutable_role_id() = (NFINetModule::NFToPB(pLoginRoleInfo->roleId));

            m_pNetModule->ProxyServerSendMsgPBToClient(NFMsg::EGameMsgID::EGMI_ACK_LOGIN, xData, pLoginRoleInfo->sockIndex);
            m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "Login successed :", pLoginRoleInfo->account, __FUNCTION__, __LINE__);
        }
    }
}

void NFCLoginNet_ServerModule::OnSelectWorldProcess(const int nSockIndex, const int nMsgID, const char* msg, const uint32_t nLen)
{
    NFMsg::ReqConnectWorld xMsg;
    if (!m_pNetModule->ProxyServerReceivePBFromClient(nSockIndex, nMsgID, msg, nLen, xMsg))
    {
        return;
    }

    NetObject* pNetObject = m_pNetModule->GetNet()->GetNetObject(nSockIndex);
    if (!pNetObject)
    {
        return;
    }

    NF_SHARE_PTR<LoginRoleInfo> pLoginInfo = GetLoginRoleInfo(pNetObject->GetClientID());
    if (pLoginInfo == nullptr)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "Select World Failed :", xMsg.account().c_str(), __FUNCTION__, __LINE__);
        return;
    }

    if (pNetObject->GetConnectKeyState() <= 0)
    {
        return;
    }

    NFMsg::ReqConnectWorld xData;
    xData.set_world_id(xMsg.world_id());
    xData.set_login_id(pPluginManager->GetAppID(NF_ST_LOGIN));
    xData.mutable_sender()->CopyFrom(NFINetModule::NFToPB(pNetObject->GetClientID()));
    xData.set_account(pLoginInfo->account);

    m_pLoginToMasterModule->SendMsgPBToMaster(pLoginInfo->account, NFMsg::EGameMsgID::EGMI_REQ_CONNECT_WORLD, xData);//here has a problem to be solve

    m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "Select World successed :", xMsg.account().c_str());
}

void NFCLoginNet_ServerModule::OnSocketClientEvent(const int nSockIndex, const NF_NET_EVENT eEvent, NFINet* pNet)
{
    if (eEvent & NF_NET_EVENT_EOF)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "NF_NET_EVENT_EOF", "Connection closed", __FUNCTION__, __LINE__);
        OnClientDisconnect(nSockIndex);
    }
    else if (eEvent & NF_NET_EVENT_ERROR)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "NF_NET_EVENT_ERROR", "Got an error on the connection", __FUNCTION__, __LINE__);
        OnClientDisconnect(nSockIndex);
    }
    else if (eEvent & NF_NET_EVENT_TIMEOUT)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "NF_NET_EVENT_TIMEOUT", "read timeout", __FUNCTION__, __LINE__);
        OnClientDisconnect(nSockIndex);
    }
    else  if (eEvent == NF_NET_EVENT_CONNECTED)
    {
        m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "NF_NET_EVENT_CONNECTED", "connectioned success", __FUNCTION__, __LINE__);
        OnClientConnected(nSockIndex);
    }
}

void NFCLoginNet_ServerModule::SynWorldToClient(const int nFD)
{
    NFMsg::AckServerList xData;

    NFMapEx<int, NFMsg::ServerInfoReport>& xWorldMap = m_pLoginToMasterModule->GetWorldMap();
    NFMsg::ServerInfoReport* pWorldData = xWorldMap.FirstNude();
    while (pWorldData)
    {
        NFMsg::ServerInfo* pServerInfo = xData.add_info();

        pServerInfo->set_name(pWorldData->server_name());
        pServerInfo->set_status(pWorldData->server_state());
        pServerInfo->set_server_id(pWorldData->server_id());
        pServerInfo->set_wait_count(0);

        pWorldData = xWorldMap.NextNude();
    }


    m_pNetModule->ProxyServerSendMsgPBToClient(NFMsg::EGameMsgID::EGMI_ACK_WORLD_LIST, xData, nFD);
}

void NFCLoginNet_ServerModule::OnViewWorldProcess(const int nSockIndex, const int nMsgID, const char* msg, const uint32_t nLen)
{
    NFMsg::ReqServerList xMsg;
    if (!m_pNetModule->ProxyServerReceivePBFromClient(nSockIndex, nMsgID, msg, nLen, xMsg))
    {
        return;
    }

    //  if (xMsg.type() == NFMsg::RSLT_WORLD_SERVER)
    //  {
    SynWorldToClient(nSockIndex);
    //  }

    m_pLogModule->LogNormal(NFILogModule::NLL_INFO_NORMAL, NFGUID(0, nSockIndex), "View World successed", "");
}

void NFCLoginNet_ServerModule::OnHeartBeat(const int nSockIndex, const int nMsgID, const char* msg, const uint32_t nLen)
{
}

void NFCLoginNet_ServerModule::OnLogOut(const int nSockIndex, const int nMsgID, const char* msg, const uint32_t nLen)
{
}

void NFCLoginNet_ServerModule::InvalidMessage(const int nSockIndex, const int nMsgID, const char* msg, const uint32_t nLen)
{
    printf("NFNet || unMsgID=%d\n", nMsgID);
}
